package org.example.utils;

import cn.hutool.json.JSONUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.Data;
import org.example.constants.CommonConstants;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.UUID;

/**
 * jwt
 */
public class JwtUtil {

    public static final Long JWT_TTL = 60 * 60 * 1000L;

    public static final String JWT_KEY = "ChArLIe";

    public static final String JWT_API_KEY = "ChArLIeAPI";


    public static final Long JWT_API_TTL = 30 * 1000L;
    public static String getUUID() {
        return UUID.randomUUID().toString().replaceAll("-", "");
    }
    public static String createJWT(String subject) {
        return getJwtBuilder(subject, null, getUUID()).compact();
    }
    public static String createJWT(String subject, Long tllMills) {
        return getJwtBuilder(subject, tllMills, getUUID()).compact();
    }

    public static JwtBuilder getJwtBuilder(String subject, Long ttlMills, String uuid) {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        SecretKey secretKey = generateKey();
        long nowMills = System.currentTimeMillis();
        Date now = new Date(nowMills);
        if (ttlMills == null) {
            ttlMills = JWT_TTL;
        }
        long expMills = ttlMills + nowMills;
        Date expireDate = new Date(expMills);
        return Jwts.builder().setId(uuid)
                .setSubject(subject)
                .setIssuer("charlie")
                .setIssuedAt(now)
                .signWith(signatureAlgorithm, secretKey)
                .setExpiration(expireDate)
                ;


    }

    private static SecretKey generateKey() {
        byte[] decode = Base64.getDecoder().decode(JWT_KEY);
        SecretKeySpec aes = new SecretKeySpec(decode, 0, decode.length, "AES");
        return aes;
    }

    private static SecretKey generateInternalApiKey() {
        byte[] decode = Base64.getDecoder().decode(JWT_API_KEY);
        SecretKeySpec aes = new SecretKeySpec(decode, 0, decode.length, "AES");
        return aes;
    }


    public static Claims parseJWT(String token){
        try {
            SecretKey secretKey = generateKey();
            return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody();
        } catch (Exception e) {
            return null;
        }
    }

    public static String createApiToken(String serviceName, String requestId) {
        long nowMills = System.currentTimeMillis();
        Date expireDate = new Date(nowMills + JWT_API_TTL);
        return Jwts.builder()
                .setSubject(serviceName)
                .setIssuer(serviceName)
                //.setAudience(getTargetServiceName(request))
                .claim("requestId",requestId)
                .setIssuedAt(new Date())
                .setExpiration(expireDate) // 30秒有效期
                .signWith( SignatureAlgorithm.HS256,generateInternalApiKey())
                .compact();
    }

    public static boolean verifyApiToken(String token) {
        try {
            SecretKey secretKey = generateInternalApiKey();
            Claims claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody();
            boolean isIn = CommonConstants.ServiceName.isIn(Opt.of(claims).exec(Claims::getSubject).orElse(""));
            return isIn && claims.getExpiration().after(new Date());
        } catch (Exception e) {
            return false;
        }
    }


    @Data
    static class User{
        private String username;
        private Integer age;
        private Double salary;
    }

    public static void main(String[] args) {
        User user = new User();
        user.setUsername("张小凡");
        user.setAge(98);
        user.setSalary(100.98d);
        String token = createJWT(JSONUtil.toJsonStr(user));
        Claims claims = parseJWT(token);
        System.out.println(token);
        System.out.println(claims);
        //javax.xml.bind.DatatypeConverter
    }
}